/**
 * graphmanager.c
 * Implementasi untuk modul graphmanager
 * Modul ini menangani arus data yang melalui graph sistem
 * Termasuk penanganan struktur data dan atribut untuk setiap nodes
 * Fri, 4 Dec 2009
 * By Karol Danutama 13508040
 * */

#include "../graphmanager/graphmanager.h"

//KONSTRUKTOR
void CreateEmptyNodeList(TNodeList *NodeList) {
	//KAMUS LOKAL
	int i;
	
	//ALGORITMA
	NodeList->NNode = 0;
	forup(i , 0 , MaxNode - 1) {
		//Mengosongkan informasi nodes
		NodeList->node[i] = CreateNode(CharToString("") , false , false , 0);
		CreateEmptyTabStudents(&(NodeList->node[i].students));
		CreateEmptyTabLectures(&(NodeList->node[i].lectures));
		CreateEmptyTabRelation(&(NodeList->node[i].lectures_students));
	}
}

void CreateEmptyAdjList(TAdjList *AdjList) {
	AdjList->NEdge = 0;
}

TNode CreateNode(string Name , boolean IsServer , boolean IsClient , float CPUTime) {
	TNode retval;
	retval.Name = Name;
	retval.isServer = IsServer;
	retval.isClient = IsClient;
	retval.CPUTime = CPUTime;
	//belum ada tabel apapun saat dibuat
	retval.IsStudentsExists = false;
	retval.IsLecturesExists = false;
	retval.IsLectures_StudentsExists = false;
	return retval;
}

void CreateEmptyAdjMatrix(TAdjMatrix m) {
	//KAMUS LOKAL
	int i , j;
	
	//ALGORITMA : adj matrix diisi nilai tak terdefinisi
	forup(i , 0 , MaxNode - 1)
		forup(j , 0 , MaxNode - 1)
			m[i][j] = null;
}

int AddNode(TNodeList *NodeList , TNode node) {
	//KAMUS LOKAL
	int physName = GetPhysicalNodeName(NodeList , node.Name);
	
	//ALGORITMA : diperiksa apakah daftar node sudah penuh atau belum dan apakah nama node sudah pernah dipakai
	//mengembalikan error code, 0 jika sukses
	if (NodeList->NNode < MaxNode - 1 && physName == -1) {
		NodeList->NNode++;
		NodeList->node[NodeList->NNode] = node;
		return 0;
	}
	else if (physName != -1)
		return 15;
	else
		return 13;
}

int AddEdge(TAdjMatrix m , TAdjList *AdjList , int org , int dest , float Bandwidth) {
	//ALGORITMA: diperiksa apakah org dan dest ada pada NodeList
	//mengembalikan error code, 0 jika sukses
	if (org != -1 && dest != -1) {
		if (Bandwidth > m[org][dest]) { //jika nilai Bandwidth lebih besar daripada yang ada di adj matrix -> lebih optimum
			m[org][dest] = Bandwidth;
			m[dest][org] = Bandwidth;
		}
		AdjList->NEdge++;
		AdjList->edge[AdjList->NEdge].org = org;
		AdjList->edge[AdjList->NEdge].dest = dest;
		AdjList->edge[AdjList->NEdge].bandwidth = Bandwidth;
		return 0;
	}
	else
		return 2;
}

int CountEdge(TAdjMatrix m , int NNode) {
	//KAMUS LOKAL
	int retval = 0 , i , j;
	
	//ALGORITMA
	forup(i , 0 , NNode)
		forup(j , j , NNode)
			if (m[i][j] != null) retval++;
	return retval;
}

int GetPhysicalNodeName(TNodeList *t , string name) {
	//KAMUS LOKAL
	int i = 1 , retval = -1;
	
	//ALGORITMA
	forup(i , 1 , t->NNode)
		if (IsStringEqual(t->node[i].Name , name)) retval = i;
	return retval;
}

string GetActualNodeName(TNodeList *t , int address) {
	if (address >= 1 && address <= t->NNode) //diperiksa apakah address berada dalam jangkauan jumlah node saat itu
		return t->node[address].Name;
	else
		return NULLValue;
}

//TABLE MANAGEMENT
int CreateStudentsTable(TNode *node) {
	//ALGORITMA : memeriksa apakah node server dan tabel mahasiswa belum ada
	//mengembalikan error code, 0 jika sukses
	if (node->isServer && !node->IsStudentsExists) {
		node->IsStudentsExists = true;
		CreateEmptyTabStudents(&(node->students));
		return 0;
	}
	else if (!node->isServer)
		return 8;
	else if (node->IsStudentsExists)
		return 14;
}


int CreateLecturesTable(TNode *node) {
	//ALGORITMA : memeriksa apakah node server dan tabel matakuliah belum ada
	//mengembalikan error code, 0 jika sukses
	if (node->isServer && !node->IsLecturesExists) {
		node->IsLecturesExists = true;
		CreateEmptyTabLectures(&(node->lectures));
		return 0;
	}
	else if (!node->isServer)
		return 8;
	else if (node->IsLecturesExists)
		return 14;
}


int CreateLectures_StudentsTable(TNode *node) {
	//ALGORITMA : memeriksa apakah node server dan tabel pengambilanmk belum ada
	//mengembalikan error code, 0 jika sukses
	if (node->isServer && !node->IsLectures_StudentsExists) {
		node->IsLectures_StudentsExists = true;
		CreateEmptyTabRelation(&(node->lectures_students));
		return 0;
	}
	else if (!node->isServer)
		return 8;
	else if (node->IsLectures_StudentsExists)
		return 14;
}


TRetvalDijkstra FindPath(TAdjMatrix m , float CPUTime , int NNode , int org , int dest , int size) {
	//KAMUS LOKAL
	TRetvalDijkstra retval;
	int i , j;
	float cost[MaxNode];
	int parent[MaxNode], currentNode;
	boolean visited[MaxNode] = {false};
	boolean found = false;
	
	
	//ALGORITMA
	retval.PathLength = 0;
	
	forup(i , 0 , NNode) cost[i] = 99999;
	forup(i , 0 , NNode) visited[i] = false;
	forup(i , 0 , NNode) parent[i] = 0;
	
	cost[org] = 0;
	currentNode = org;
	do {
		forup(j , 1 , NNode) {
			if (m[currentNode][j] != -1 && !visited[j]) {
				if (cost[currentNode] + (size / (m[currentNode][j] * 1000)) < cost[j]) {
					cost[j] = cost[currentNode] + (size / (m[currentNode][j] * 1000));
					parent[j] = currentNode;
				}
			}
		}
		visited[currentNode] = true;
		
		currentNode = 0;
		found = false;
		forup(i , 1 , NNode) {
			if (!visited[i] && cost[i] < cost[currentNode] && i != dest) {
				currentNode = i;
				found = true;
			}
		}
	} while (found);
	
	if (parent[dest] != 0 || org == dest) { //jika sampai ke tujuan -> parent[dest] != 0 atau org memang sama dengan dest
		//mengisi path
		i = dest;
		while (i != org) {
			retval.PathLength++;
			retval.path[retval.PathLength] = i;
			i = parent[i];
		}
		//terminasi
		retval.PathLength++;
		retval.path[retval.PathLength] = i;
		retval.Cost = cost[dest] + CPUTime;
	}
	else { //tidak ditemukan jalur dari org ke dest -> parent[dest] == 0 dan org != dest
		retval.Cost = null;
		retval.PathLength = 0;
	}
	
	return retval;
}

void PrintNodeInfo(TNode *node) {
	if (node->IsStudentsExists) 
		printf("%-9.9s      %d      %d %0.4f   %-13.13s   %d \n" , node->Name.s , node->isServer , node->isClient , node->CPUTime , StudentsTable.s , node->students.NStudents);
		
	if (node->IsLecturesExists && !node->IsStudentsExists) 
		printf("%-9.9s      %d      %d %0.4f   %-13.13s   %d \n" , node->Name.s , node->isServer , node->isClient , node->CPUTime , LecturesTable.s , node->lectures.NLectures);
	else if (node->IsLecturesExists)
		printf("                                 %-13.13s   %d \n" , LecturesTable.s , node->lectures.NLectures);
	
	if (node->IsLectures_StudentsExists && !node->IsStudentsExists && !node->IsLecturesExists) 
		printf("%-9.9s      %d      %d %0.4f   %-13.13s   %d \n" , node->Name.s , node->isServer , node->isClient , node->CPUTime , Lectures_StudentsTable.s , node->lectures_students.NRelation);
	else if (node->IsLectures_StudentsExists)
		printf("                                 %-13.13s   %d \n" , Lectures_StudentsTable.s , node->lectures_students.NRelation);
	
	if (!(node->IsStudentsExists || node->IsLecturesExists || node->IsLectures_StudentsExists)) 
		printf("%-9.9s      %d      %d %0.4f\n" , node->Name.s , node->isServer , node->isClient , node->CPUTime);
}

void PrintNodeList(TNodeList *nodes , boolean IsAsc) {
	//KAMUS LOKAL
	int i;
	
	//ALGORITMA
	printf("Name      Server Client CPUTime  Table           No of records\n");
	if (IsAsc)
		forup(i , 1 , nodes->NNode)
			PrintNodeInfo(&(nodes->node[i]));
	else
		fordown(i , nodes->NNode , 1)
			PrintNodeInfo(&(nodes->node[i]));
}

void PrintPath(TRetvalDijkstra r , TNodeList *nodes) {
	//KAMUS LOKAL
	int i;
	
	//ALGORITMA
	forup(i , 1 , r.PathLength) {
		printf("%s" , nodes->node[r.path[i]].Name.s);
		(i < r.PathLength) ? printf(" - ") : printf("\n");
	}
}

void PrintAdjList(TAdjList AdjList , TNodeList *nodes , boolean IsASC) {
	//KAMUS LOKAL
	int i;
	string org , dest;
	
	//ALGORITMA
	if (IsASC)
		forup(i , 1 , AdjList.NEdge) {
			org = GetActualNodeName(nodes , AdjList.edge[i].org);
			dest = GetActualNodeName(nodes , AdjList.edge[i].dest);
			printf("%s %s %f\n" , org.s , dest.s , AdjList.edge[i].bandwidth);
		}
	else
		fordown(i , AdjList.NEdge , 1) {
			org = GetActualNodeName(nodes , AdjList.edge[i].org);
			dest = GetActualNodeName(nodes , AdjList.edge[i].dest);
			printf("%s %s %f\n" , org.s , dest.s , AdjList.edge[i].bandwidth);
		}
}

void PrintAdjMatrix(TAdjMatrix m , int NNode) {
	//KAMUS LOKAL
	int i , j;
	
	//ALGORITMA
	forup(i , 1 , NNode) {
		forup(j , 1 , NNode)
			printf("%f " , m[i][j]);
		printf("\n");
	}
}


//SORTER
TNodeList SortNodeListByName(TNodeList *nodes) {
	//KAMUS LOKAL
	TNodeList retval;
	int i , j , imin;
	TNode temp;
	
	//ALGORITMA : minsort
	retval = *nodes;
	
	forup(i , 1 , retval.NNode - 1) {
		imin = i;
		forup(j , i + 1 , retval.NNode)
			imin = (IsStringLt(retval.node[j].Name , retval.node[imin].Name)) ? j : imin;
		temp = retval.node[i];
		retval.node[i] = retval.node[imin];
		retval.node[imin] = temp;
	}
	
	return retval;
}

TAdjList SortAdjList(TAdjList A) {
	//KAMUS LOKAL
	TAdjList retval = A;
	TEdge temp;
	int i , j , imin;
	
	//ALGORITMA : minsort
	forup(i , 1 , retval.NEdge - 1) {
		imin = i;
		forup(j , i + 1 , retval.NEdge) {
			imin = (retval.edge[j].bandwidth < retval.edge[imin].bandwidth) ? j : imin;
		}
		temp = retval.edge[i];
		retval.edge[i] = retval.edge[imin];
		retval.edge[imin] = temp;
	}
	
	return retval;
}

//FILTER
TNodeList FilterServers(TNodeList *nodes) {
	//KAMUS LOKAL
	TNodeList retval;
	int i , j;
	
	//ALGORITMA
	CreateEmptyNodeList(&retval);
	
	forup(i , 1 , nodes->NNode)
		if (nodes->node[i].isServer)
			AddNode(&retval , nodes->node[i]);
	
	return retval;
}

TNodeList FilterClients(TNodeList *nodes) {
	//KAMUS LOKAL
	TNodeList retval;
	int i , j;
	
	//ALGORITMA
	CreateEmptyNodeList(&retval);
	
	forup(i , 1 , nodes->NNode)
		if (nodes->node[i].isClient)
			AddNode(&retval , nodes->node[i]);
	
	return retval;
}
